﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Net;
using System.Diagnostics;
using System.Net.Sockets;
using Microsoft.Win32;

namespace NeverFeel
{
	public partial class MainNewForm : NeverFeel.BaseForm
	{
		public static string AppName = "Server Status";
		private Icon radioOn;
		private Icon radioOnPartial;
		private Icon radioOff;
		
		private ScanState state = ScanState.Pending;
		private string myStatus = "";
		private object winAmpPath;
		
		private const string MSG_CANT_CONNECT = "Can't connect";
		private const string MSG_CONNECTED = "Connect successful";
		private const string MSG_UNKNOWN = "Unknown";

		private const string RS_UNKNOWN = "Unknown state";
		private const string RS_CONNECTING = "Connecting..";
		private const string RS_CONNECTED = "Online";
		private const string RS_CANT_CONNECT = "Offline";
		private const int ERROR_FILE_NOT_FOUND = 2;
		private const int ERROR_ACCESS_DENIED = 5;

		
		#region additional classes
		private enum ScanState
		{
			Pending,
			Scanning,
			Canceled,
		}

		public class Server
		{
			public IPAddress IP;
			public string Name;
			public int Port;
			public bool Enabled;

			public Server(IPAddress ip, int port, bool enabled) : this("", ip, port, enabled)
			{

			}

			public Server(string name, IPAddress ip, int port, bool enabled)
			{
				this.Name = name;
				this.IP = ip;
				this.Port = port;
				this.Enabled = enabled;
			}
		}

		class ShowProgressArgs : EventArgs
		{
			public string LogMessage;
			public Server server;
			public bool Cancel;
			public bool ScanComplete = false;

			public ShowProgressArgs(string logMessage, Server server)
			{
				this.LogMessage = logMessage;
				this.server = server;
			}
		}

		delegate void ShowProgressHandler(object sender, ShowProgressArgs e);
		delegate ScanResult ScanServerDelegate(Server server);
		delegate void ScanAllDelegate(ArrayList servers);
		delegate void AddLogDelegate(IPAddress ip, int errCode, string errMessage);

		public class ScanResult
		{
			private Server server;
			private bool result = false;
			private string message = "";

			public ScanResult(Server server)
			{
				this.server = server;
			}

			public ScanResult(Server server, bool result, string message)
			{
				this.server = server;
				this.result = result;
				this.message = message;
			}

			public Server Server
			{
				get
				{
					return server;
				}
				set
				{
					this.server = value;
				}
			}


			public bool Result
			{
				get
				{
					return result;
				}
				set
				{
					this.result = value;
				}
			}

			public string Message
			{
				get
				{
					return message;
				}
				set
				{
					this.message = value;
				}
			}
		}
		#endregion


		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main()
		{
			MainNewForm mf = new MainNewForm();
			Application.Run(mf);
		}


		public MainNewForm()
		{
			try
			{
				radioOn = new Icon(this.GetType().Assembly.GetManifestResourceStream("NeverFeel.radioon.ico"));
				radioOnPartial = new Icon(this.GetType().Assembly.GetManifestResourceStream("NeverFeel.radioonp.ico"));
				radioOff = new Icon(this.GetType().Assembly.GetManifestResourceStream("NeverFeel.radiooff.ico"));
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, "Error Loading Some Resources!");
			}

			InitializeComponent();
			
			listView1.ContextMenu = contextMenu2;
			notifyIcon1.Icon = radioOff;

			winAmpPath = null;
			try
			{
				winAmpPath = Registry.ClassesRoot.OpenSubKey("CLSID").OpenSubKey("{46986115-84D6-459c-8F95-52DD653E532E}").OpenSubKey("LocalServer32").GetValue("");
				if (winAmpPath != null)
				{
					ListenMenuItem.Enabled = EnqueueMenuItem.Enabled = true;
				}
			}
			catch { }


			CheckRegistryEntry();
			
			timer1.Enabled = true;
			timer1.Tick += new EventHandler(timer1_Tick);

			EnableScanBox.Checked = true;
			StartScanThread();
		}

		void listView1_MouseDoubleClick(object sender, MouseEventArgs e)
		{
			MessageBox.Show("Test");
		}


		#region registry

		/// <summary>
		/// Сохраняем данные в реестр
		/// </summary>
		private void SaveToRegistry()
		{
			RegistryKey myMainKey = Registry.CurrentUser.OpenSubKey("Software", true);
			RegistryKey mySoftKey = myMainKey.OpenSubKey("NeverFeel");
			if (mySoftKey == null)
			{
				mySoftKey = myMainKey.CreateSubKey("NeverFeel");
			}

			RegistryKey myRadioKey = mySoftKey.OpenSubKey("ServerStatus", true);
			if (myRadioKey == null)
			{
				myRadioKey = mySoftKey.CreateSubKey("ServerStatus");
			}

			Object myIntervalValue = myRadioKey.GetValue("Interval");
			myRadioKey.SetValue("Interval", TimeIntervalBox.Value.ToString());
			timer1.Interval = 1000 * Int32.Parse(TimeIntervalBox.Value.ToString());

			RegistryKey myHostsKey = myRadioKey.OpenSubKey("Hosts", true);
			if (myHostsKey == null)
			{
				myHostsKey = myRadioKey.CreateSubKey("Hosts");
			}

			foreach (string valueName in myHostsKey.GetValueNames())
			{
				myHostsKey.DeleteValue(valueName, false);
			}

			foreach (ListViewItem myItem in listView1.Items)
			{
				string tmpValue = "";
				string myName = myItem.SubItems[0].Text;
				string myHost = myItem.SubItems[1].Text;
				string myPort = myItem.SubItems[2].Text;
				bool myChecked = myItem.Checked;
				string myCheckedChar = "0";

				if (myChecked)
				{
					myCheckedChar = "1";
				}

				tmpValue = myHost + ":" + myPort + ":" + myCheckedChar;

				myHostsKey.SetValue(myName, tmpValue);
			}
		}


		/// <summary>
		/// Проверяем записи в реестре
		/// </summary>
		private void CheckRegistryEntry()
		{
			RegistryKey myMainKey = Registry.CurrentUser.OpenSubKey("Software", true);
			RegistryKey mySoftKey = myMainKey.OpenSubKey("NeverFeel");
			if (mySoftKey == null)
			{
				mySoftKey = myMainKey.CreateSubKey("NeverFeel");
			}

			RegistryKey myRadioKey = mySoftKey.OpenSubKey("ServerStatus", true);
			if (myRadioKey == null)
			{
				myRadioKey = mySoftKey.CreateSubKey("ServerStatus");
			}

			Object myIntervalValue = myRadioKey.GetValue("Interval");
			if (myIntervalValue == null)
			{
				myRadioKey.SetValue("Interval", "60");
				myIntervalValue = "60";
			}
			timer1.Interval = 1000 * Int32.Parse(myIntervalValue.ToString());
			TimeIntervalBox.Value = Int32.Parse(myIntervalValue.ToString());

			RegistryKey myHostsKey = myRadioKey.OpenSubKey("Hosts", true);
			if (myHostsKey == null)
			{
				myHostsKey = myRadioKey.CreateSubKey("Hosts");
			}


			if (myHostsKey.ValueCount > 0)
			{
				listView1.Items.Clear();
				foreach (string valueName in myHostsKey.GetValueNames())
				{
					string tmpValue = "";
					string myHost = "";
					string myName = "";
					string myPort = "";
					bool myChecked = false;

					tmpValue = myHostsKey.GetValue(valueName).ToString();

					int firstD = tmpValue.IndexOf(":");
					int lastD = tmpValue.LastIndexOf(":");

					myName = valueName;
					myHost = tmpValue.Substring(0, firstD);
					myPort = tmpValue.Substring(firstD + 1, (lastD - firstD) - 1);

					if (tmpValue.Substring(lastD + 1) == "1")
					{
						myChecked = true;
					}

					ListViewItem myItem = new ListViewItem(myName, 0);
					myItem.SubItems.Add(myHost);
					myItem.SubItems.Add(myPort);
					myItem.Checked = myChecked;
					listView1.Items.Add(myItem);
				}
			}

		}

		#endregion


		private bool ScanEnabled
		{
			get
			{
				return EnableScanBox.Checked;
			}
		}
		

		private void AddServerMenuItem_Click(object sender, EventArgs e)
		{
			string name = "", host = "";
			int port = 0;

			if (AddServerForm.GetNewServer(ref name, ref host, ref port))
			{
				ListViewItem myItem = new ListViewItem(name, 0);
				myItem.SubItems.Add(host);
				myItem.SubItems.Add(port.ToString());
				myItem.Checked = true;
				listView1.Items.Add(myItem);
				SaveToRegistry();
			}
		}

		private void ApplyBtn_Click(object sender, EventArgs e)
		{
			SaveToRegistry();
		}

		private void ScanBtn_Click(object sender, EventArgs e)
		{
			StartScanThread();
		}

		private void EnableScanBox_CheckedChanged(object sender, EventArgs e)
		{
			TimeIntervalBox.Enabled = EnableScanBox.Checked;
		}

		private void timer1_Tick(object sender, EventArgs e)
		{
			if (ScanEnabled)
			{
				StartScan();
			}
		}

		private void StartScan()
		{
			StartScanThread();
		}


		public void StartScanThread()
		{
			LogBox.Clear();
			//DeleteMenuItem.Enabled = false;

			this.notifyIcon1.Text = AvailableServersCount.ToString() + " servers available. Scanning..";
			this.LogBox.Clear();

			ArrayList servers = new ArrayList();
			foreach (ListViewItem myItem in listView1.Items)
			{
				if (myItem.Checked)
				{
					Server server = new Server(myItem.Text, IPAddress.Parse(myItem.SubItems[1].Text), Int32.Parse(myItem.SubItems[2].Text), false);
					servers.Add(server);

					// Асинхр. делегат
					ScanServerDelegate processScan = new ScanServerDelegate(Process);
					processScan.BeginInvoke(server, new AsyncCallback(ScanServerCallback), processScan);
				}
			}
		}


		/// <summary>
		/// Сканирует указанный IP по указанному порту
		/// </summary>
		private ScanResult Process(Server server)
		{
			ScanResult result = new ScanResult(server);

			IPEndPoint ep = new IPEndPoint(server.IP, server.Port);
			Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

			try
			{
				socket.Connect(ep);
				result.Result = socket.Connected;
				result.Server.Enabled = result.Result;
				result.Message = result.Result ? "OK\r\n" : "Unable to connect\r\n";
			}
			catch (Exception ex)
			{
				result.Message = ex.Message;
			}
			finally
			{
				socket.Close();
				GC.Collect();
			}

			return result;
		}


		private void ScanAllCallback(IAsyncResult result)
		{
			// переводим вызов в главный поток
			if (InvokeRequired)
			{
				Invoke(new AsyncCallback(ScanAllCallback), new object[] { result });
				return;
			}

			lock (this.LogBox)
			{
				// завершаем вызов и получаем данные
				ScanAllDelegate scanResult = (ScanAllDelegate)result.AsyncState;
				scanResult.EndInvoke(result);

				LogBox.Text += "\r\n" + " == Scan complete == ";
			}
		}

		private void AddLog(IPAddress ip, int errCode, string errMessage)
		{
			if (InvokeRequired)
			{
				Invoke(new AddLogDelegate(AddLog), ip, errCode, errMessage);
				return;
			}

			if (errCode == -1)
			{
				notifyIcon1.Icon = radioOnPartial;
			}
			else
			{
				//notifyIcon1.Icon = radioOff;
			}
			DateTime myTime = DateTime.Now;
			myStatus = "> " + myTime.Hour + ":" + myTime.Minute + ":" + myTime.Second + " " + ip.ToString() + " " + errMessage.ToString() + "\r\n";
			
			LogBox.AppendText(myStatus);
		}


		private int AvailableServersCount
		{
			get
			{
				int count = 0;
				foreach (ListViewItem myItem in listView1.Items)
				{
					if (myItem.Checked)
					{
						if (myItem.Tag!= null && Convert.ToBoolean(myItem.Tag))
							count++;
					}
				}

				return count;
			}
		}

		private int ServersToCheckCount
		{
			get
			{
				int count = 0;
				foreach (ListViewItem myItem in listView1.Items)
				{
					if (myItem.Checked)
					{
						count++;
					}
				}

				return count;
			}
		}



		private void ScanServerCallback(IAsyncResult result)
		{
			// переводим вызов в главный поток
			if (InvokeRequired)
			{
				Invoke(new AsyncCallback(ScanServerCallback), new object[] { result });
				return;
			}

			// завершаем вызов и получаем данные
			ScanServerDelegate scanResult = (ScanServerDelegate)result.AsyncState;
			ScanResult res = scanResult.EndInvoke(result);

			AddLog(res.Server.IP, 0, res.Message);
			
			if (!res.Result)
			{
				//notifyIcon1.ShowBalloonTip(8000, AppName, "Сервер " + res.Server.IP.ToString() + " не доступен", ToolTipIcon.Warning);
			}
			else
			{
				//
			}

			foreach (ListViewItem myItem in listView1.Items)
			{
				if (myItem.SubItems[1].Text.ToString() == res.Server.IP.ToString() && Int32.Parse(myItem.SubItems[2].Text) == res.Server.Port)
				{
					myItem.Tag = res.Result ? true : false;
					int oldIndex = myItem.ImageIndex;
					int newIndex = res.Result ? 1 : 0;
					myItem.ImageIndex = newIndex;

					if (newIndex != oldIndex)
					{
						notifyIcon1.ShowBalloonTip(8000, AppName, "Server " + res.Server.Name + " (" + res.Server.IP.ToString() + ")" + (res.Result ? " available" : " unavailable"), res.Result ? ToolTipIcon.Info : ToolTipIcon.Warning);
					}
				}
			}

			object sender = System.Threading.Thread.CurrentThread;
			ShowProgressArgs e = new ShowProgressArgs("Scan of " + res.Server.Name + "(" + res.Server.IP.ToString() + ") complete..\r\n", res.Server);
			ShowProgress(sender, e);
		}


		void ShowProgress(object sender, ShowProgressArgs e)
		{
			if (this.InvokeRequired == false)
			{
				LogBox.Text += e.LogMessage;

				// Проверка на отмену
				e.Cancel = (state == ScanState.Canceled);

				this.notifyIcon1.Text = AvailableServersCount.ToString() + " servers available";
				if (ServersToCheckCount == 0 || AvailableServersCount == 0)
				{
					this.notifyIcon1.Icon = radioOff;
				}
				else if (ServersToCheckCount == AvailableServersCount)
				{
					this.notifyIcon1.Icon = radioOn;
				}
				else
				{
					this.notifyIcon1.Icon = radioOnPartial;
				}
			}
			else
			{
				ShowProgressHandler
					showProgress =
						new ShowProgressHandler(ShowProgress);
				BeginInvoke(showProgress, new object[] { sender, e });
			}
		}

		private void miProperties_Click(object sender, EventArgs e)
		{
			ShowForm();
		}

		private void ListenMenuItem_Click(object sender, EventArgs e)
		{
			if (listView1.SelectedItems.Count > 0)
			{
				PlayInWinAmp("http://" + listView1.SelectedItems[0].SubItems[1].Text + ":" + listView1.SelectedItems[0].SubItems[2].Text);
			}
		}

		private void EnqueueMenuItem_Click(object sender, EventArgs e)
		{
			if (listView1.SelectedItems.Count > 0)
			{
				PlayInWinAmp("/ADD http://" + listView1.SelectedItems[0].SubItems[1].Text + ":" + listView1.SelectedItems[0].SubItems[2].Text);
			}
		}

		#region PlayInWinAmp
		/// <summary>
		/// Загружает WinAmp
		/// </summary>
		/// <param name="arg"></param>
		private void PlayInWinAmp(string arg)
		{
			try
			{
				Process myProcess = new Process();
				// Get the path that stores user documents.

				myProcess.StartInfo.FileName = winAmpPath.ToString();
				myProcess.StartInfo.Arguments = arg;
				myProcess.StartInfo.UseShellExecute = true;
				myProcess.Start();
			}
			catch (Win32Exception ex)
			{
				if (ex.NativeErrorCode == ERROR_FILE_NOT_FOUND)
				{
					MessageBox.Show(ex.Message + ". WinAmp not installed.");
				}

				else if (ex.NativeErrorCode == ERROR_ACCESS_DENIED)
				{
					MessageBox.Show(ex.Message +
						". You do not have permission to execute this file.");
				}
			}
		}
		#endregion

		private void DeleteMenuItem_Click(object sender, EventArgs e)
		{
			if (listView1.SelectedItems.Count > 0
				&& (MessageBox.Show("Are you really want to delete server " + listView1.SelectedItems[0].Text + "?", AppName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes))
			{
				listView1.SelectedItems[0].Remove();
			}
			SaveToRegistry();
		}

		private void notifyIcon1_DoubleClick(object sender, EventArgs e)
		{
			if (this.WindowState == FormWindowState.Normal)
				HideForm();
			else
				ShowForm();
		}

		private void miExit_Click(object sender, EventArgs e)
		{
			timer1.Enabled = false;
			notifyIcon1.Visible = false;
			Application.Exit();
		}

		private void CopyIPMenuItem_Click(object sender, EventArgs e)
		{
			if (InvokeRequired)
			{
				Invoke(new System.EventHandler(this.CopyIPMenuItem_Click), sender, e);
				return;
			}

			if (listView1.SelectedItems.Count > 0)
			{
				Clipboard.SetDataObject(listView1.SelectedItems[0].SubItems[1].Text + ":" + listView1.SelectedItems[0].SubItems[2].Text);
			}
		}

		private void AboutMenuItem_Click(object sender, EventArgs e)
		{
			MessageBox.Show(AppName + ", 2004 http://NeverFeel.ru", "About", MessageBoxButtons.OK, MessageBoxIcon.Information);
		}

		private void listView1_SelectedIndexChanged(object sender, EventArgs e)
		{
			
		}

		private void listView1_DoubleClick(object sender, EventArgs e)
		{
			EditServer();
		}

		private void EditServer()
		{
			if (listView1.SelectedItems.Count == 0)
				return;

			ListViewItem item = listView1.SelectedItems[0];
			string name = item.Text.ToString(), host = item.SubItems[1].Text.ToString();
			int port = Convert.ToInt32(item.SubItems[2].Text);

			if (AddServerForm.GetNewServer(ref name, ref host, ref port))
			{
				item.Text = name;
				item.SubItems[1].Text = host;
				item.SubItems[2].Text = port.ToString();
				SaveToRegistry();
			}
		}

		private void EditServerMenuItem_Click(object sender, EventArgs e)
		{
			EditServer();
		}

		private void listView1_ItemChecked(object sender, ItemCheckedEventArgs e)
		{
			SaveToRegistry();
		}
	}
}
